package com.phiture.erp.basic.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.phiture.erp.basic.core.pojo.dto.ErpMaterialInfoDTO;
import com.phiture.erp.basic.core.pojo.dto.ErpMaterialPageReqDTO;
import com.phiture.erp.basic.core.pojo.dto.ErpMaterialSaveReqDTO;
import com.phiture.erp.basic.core.pojo.vo.ErpMaterialRespVO;
import com.phiture.erp.basic.core.service.ErpMaterialCategoryService;
import com.phiture.erp.basic.core.service.ErpMaterialService;
import com.phiture.erp.basic.core.service.ErpMaterialUnitService;
import com.phiture.erp.basic.dal.entity.ErpMaterialCategoryDO;
import com.phiture.erp.basic.dal.entity.ErpMaterialDO;
import com.phiture.erp.basic.dal.entity.ErpMaterialPlanDO;
import com.phiture.erp.basic.dal.entity.ErpMaterialUnitDO;
import com.phiture.erp.basic.dal.mapper.ErpMaterialMapper;
import com.phiture.erp.common.constants.ErrorCodeConstants;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static com.phiture.erp.common.constants.ErrorCodeConstants.MATERIAL_NOT_ENABLE;
import static com.phiture.erp.common.constants.ErrorCodeConstants.MATERIAL_NOT_EXISTS;

/**
 * @author YH
 * @date 2025/3/31
 */
@Service
public class ErpMaterialServiceImpl implements ErpMaterialService {

    @Resource
    private ErpMaterialMapper materialMapper;

    @Lazy
    @Resource
    private ErpMaterialUnitService materialUnitService;

    @Lazy
    @Resource
    private ErpMaterialCategoryService materialCategoryService;

    @Override
    public Long createMaterial(ErpMaterialSaveReqDTO reqDTO) {

        // 自动生成物料编码
        reqDTO.setBarCode(generateMaterialCode(reqDTO));
        // 如果fid不存在， 名称，条码不能重复（新增）
        if (reqDTO.getId() == null) {
            selectListByNamesOrBarcodes(Collections.singletonList(reqDTO.getName()),
                    Collections.singletonList(reqDTO.getBarCode()))
                    .forEach(material -> {
                        if (material.getName().equals(reqDTO.getName())) {
                            throw exception(ErrorCodeConstants.MATERIAL_NAME_EXISTS, reqDTO.getName());
                        }

                        if (material.getBarCode().equals(reqDTO.getBarCode())) {
                            throw exception(ErrorCodeConstants.MATERIAL_BARCODE_EXISTS, reqDTO.getBarCode());
                        }
                    });
        }
        // 插入
        ErpMaterialDO product = BeanUtils.toBean(reqDTO, ErpMaterialDO.class);
        materialMapper.insertOrUpdate(product);
        // 返回
        return product.getId();
    }

    // 编写一个方法，自动生成物料编码， 规则为：物料类别的编码+"."+(当前物料类别的最大物料编码+1 格式为 0001)
    // 例如：物料类别编码为 1001， 当前物料类别的最大物料编码为 0001， 则生成的物料编码为 1001.0002
    private String generateMaterialCode(ErpMaterialSaveReqDTO reqDTO) {
        // 1. 获取物料类别编码
        ErpMaterialCategoryDO category = materialCategoryService.getMaterialCategory(reqDTO.getCategoryId());
        String categoryCode = category.getCode();
        // 2. 获取当前物料类别的最大物料编码
        MPJLambdaWrapperX<ErpMaterialDO> queryWrapper = new MPJLambdaWrapperX<>();
        queryWrapper.selectMax(ErpMaterialDO::getBarCode, "barCode");
        queryWrapper.eq(ErpMaterialDO::getCategoryId, reqDTO.getCategoryId());
        queryWrapper.groupBy(ErpMaterialDO::getCategoryId);
        ErpMaterialDO maxCode = materialMapper.selectOne(queryWrapper);
        if(maxCode == null) {
            return categoryCode + ".0001";
        }else{
            // 截取maxCode.getBarCode()最后四位数字
            String maxCodeStr = maxCode.getBarCode().substring(maxCode.getBarCode().length() - 4);
            // 3. 生成物料编码
            String materialCode = categoryCode + "." + (maxCodeStr == null ? "0001" : String.format("%04d", Integer.parseInt(maxCodeStr) + 1));
            return materialCode;
        }
    }



    @Override
    public void updateMaterial(ErpMaterialSaveReqDTO reqDTO) {
        // 校验物料类别
        materialCategoryService.validateMaterialCategoryExist(reqDTO.getCategoryId());
        // 校验存在
        validateMaterialExists(reqDTO.getId());
        // 更新
        ErpMaterialDO updateObj = BeanUtils.toBean(reqDTO, ErpMaterialDO.class);
        materialMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterial(Long id) {
        // 校验存在
        validateMaterialExists(id);
        // 删除
        materialMapper.deleteById(id);
    }

    @Override
    public List<ErpMaterialDO> validMaterilaList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<ErpMaterialDO> list = materialMapper.selectBatchIds(ids);
        Map<Long, ErpMaterialDO> materialMap = convertMap(list, ErpMaterialDO::getId);
        for (Long id : ids) {
            ErpMaterialDO materialDO = materialMap.get(id);
            if (materialMap.get(id) == null) {
                throw exception(MATERIAL_NOT_EXISTS);
            }
            if (CommonStatusEnum.isDisable(materialDO.getStatus())) {
                throw exception(MATERIAL_NOT_ENABLE, materialDO.getName());
            }
        }
        return list;
    }

    @Override
    public ErpMaterialDO getMaterial(Long id) {
        return materialMapper.selectById(id);
    }

    @Override
    public List<ErpMaterialRespVO> getMaterialListByStatusAndAttr(Integer status, Integer attr){

        // 使用 MPJLambdaWrapperX 构建关联查询条件
        MPJLambdaWrapperX<ErpMaterialDO> queryWrapper = new MPJLambdaWrapperX<>();
        queryWrapper.selectAll(ErpMaterialDO.class); // 选择物料基础表的所有字段
        queryWrapper.innerJoin(ErpMaterialPlanDO.class,
                ErpMaterialPlanDO::getMaterialId,
                ErpMaterialDO::getId); // 关联物料计划表
        if (status != null) {
            queryWrapper.eq(ErpMaterialDO::getStatus, status);
        }
        if (attr != null) {
            queryWrapper.eq(ErpMaterialPlanDO::getMaterialAttr, attr);
        }

        List<ErpMaterialDO> list = materialMapper.selectJoinList(ErpMaterialDO.class, queryWrapper);
        return buildMaterialVOList(list);
    }

    @Override
    public List<ErpMaterialRespVO> getMaterialListByStatus(Integer status) {
        List<ErpMaterialDO> list = materialMapper.selectList(ErpMaterialDO::getStatus, status);
        return buildMaterialVOList(list);
    }

    @Override
    public List<ErpMaterialInfoDTO> getMaterialList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<ErpMaterialDO> list = materialMapper.selectBatchIds(ids);
        return buildMaterialInfoList(list);
    }

    @Override
    public PageResult<ErpMaterialRespVO> getMaterialPage(ErpMaterialPageReqDTO pageReqDTO) {
        PageResult<ErpMaterialDO> pageResult = materialMapper.selectPage(pageReqDTO, new LambdaQueryWrapperX<ErpMaterialDO>()
                .likeIfPresent(ErpMaterialDO::getName, pageReqDTO.getName())
                .eqIfPresent(ErpMaterialDO::getCategoryId, pageReqDTO.getCategoryId())
                .eqIfPresent(ErpMaterialDO::getStatus, pageReqDTO.getMaterialStatus())
                .betweenIfPresent(ErpMaterialDO::getCreateTime, pageReqDTO.getCreateTime())
                .orderByDesc(ErpMaterialDO::getId));
        return new PageResult<>(buildMaterialVOList(pageResult.getList()), pageResult.getTotal());
    }

    @Override
    public Long getMaterialCountByCategoryId(Long categoryId) {
        return materialMapper.selectCount(ErpMaterialDO::getCategoryId, categoryId);
    }

    @Override
    public Long getMaterialCountByUnitId(Long unitId) {
        return materialMapper.selectCount(ErpMaterialDO::getUnitId, unitId);
    }

    @Override
    public Map<Long, String> getMaterialMap() {
        List<ErpMaterialDO> list = materialMapper.selectList(
                Wrappers.lambdaQuery(ErpMaterialDO.class)
                        .eq(ErpMaterialDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
                        .eq(ErpMaterialDO::getDeleted, Boolean.FALSE)
        );
        return convertMap(list, ErpMaterialDO::getId, ErpMaterialDO::getName);
    }

    private List<ErpMaterialDO> selectListByNamesOrBarcodes(List<String> names, List<String> barcodes) {
        LambdaQueryWrapperX<ErpMaterialDO> queryWrapper = new LambdaQueryWrapperX<>();
        if (names != null && !names.isEmpty()) {
            queryWrapper.in(ErpMaterialDO::getName, names);
        }
        if (barcodes != null && !barcodes.isEmpty()) {
            if (names != null && !names.isEmpty()) {
                queryWrapper.or();
            }
            queryWrapper.in(ErpMaterialDO::getBarCode, barcodes);
        }
        return materialMapper.selectList(queryWrapper);
    }

    private void validateMaterialExists(Long id) {
        if (materialMapper.selectById(id) == null) {
            throw exception(MATERIAL_NOT_EXISTS);
        }
    }

    private List<ErpMaterialRespVO> buildMaterialVOList(List<ErpMaterialDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        Map<Long, ErpMaterialCategoryDO> categoryMap = materialCategoryService.getMaterialCategoryMap(
                convertSet(list, ErpMaterialDO::getCategoryId));
        Map<Long, ErpMaterialUnitDO> unitMap = materialUnitService.getMaterialUnitMap(
                convertSet(list, ErpMaterialDO::getUnitId));
        return BeanUtils.toBean(list, ErpMaterialRespVO.class, material -> {
            MapUtils.findAndThen(categoryMap, material.getCategoryId(),
                    category -> material.setCategoryName(category.getName()));
            MapUtils.findAndThen(unitMap, material.getUnitId(),
                    unit -> material.setUnitName(unit.getName()));
        });
    }

    private List<ErpMaterialInfoDTO> buildMaterialInfoList(List<ErpMaterialDO> list){
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        Map<Long, ErpMaterialCategoryDO> categoryMap = materialCategoryService.getMaterialCategoryMap(
                convertSet(list, ErpMaterialDO::getCategoryId));
        Map<Long, ErpMaterialUnitDO> unitMap = materialUnitService.getMaterialUnitMap(
                convertSet(list, ErpMaterialDO::getUnitId));
        return BeanUtils.toBean(list, ErpMaterialInfoDTO.class, material -> {
            MapUtils.findAndThen(categoryMap, material.getCategoryId(),
                    category -> material.setCategoryName(category.getName()));
            MapUtils.findAndThen(unitMap, material.getUnitId(),
                    unit -> material.setUnitName(unit.getName()));
        });
    }
}
